import { Scene } from 'phaser';
import { Socket } from 'socket.io-client';
import '../customSprite/ball';

export class MainScene extends Scene {

    private speed = 200
    private myBall: Phaser.Types.Physics.Arcade.SpriteWithDynamicBody
    private balls: Array<Phaser.Types.Physics.Arcade.SpriteWithDynamicBody> = []
    private socketRef: React.MutableRefObject<Socket | null>
    private frameNumber: number = 0
    private frameNumberForUpdate: number = 0
    private players: Array<{ id: string, nickName: string, ballColor: number, x: number, y: number }> = []
    private moveKeys: Record<string, any>
    private frameNumberOperationMap = new Map()
    private lock: "open" | "close" = "open"

    constructor() {
        super('MainScene');
    }

    init({ socketRef, players }: { socketRef: React.MutableRefObject<Socket | null>, players: any }) {
        // console.log('MainScene-init', socketRef.current, players)
        this.socketRef = socketRef
        this.players = players
    }

    create() {
        console.log('MainScene-create')

        const map = this.make.tilemap({ key: 'wallMap' })
        const tiles = map.addTilesetImage('wallTileset', 'wallImg')
        const layer = map.createLayer(0, tiles, 0, 0)
        layer?.setCollision(17)
        this.socketRef.current?.on('receiveOperation', this.receiveOperation)
        this.socketRef.current?.on('receiveFrameNumber', this.receiveFrameNumber)
        this.socketRef.current?.on('receiveBatchFrameOperation', this.receiveBatchFrameOperation)

        this.players.forEach(player => {
            if (player.id === this.socketRef.current?.id) {
                const { ballColor, x, y } = player
                const ball = this.add.ball(x, y, 'ball', ballColor)
                ball.player = player
                this.physics.add.collider(layer, ball)

                this.cameras.main.startFollow(ball)

                this.myBall = ball
                this.balls.push(ball)
            } else {
                const index = this.balls.findIndex((ball) => {
                    return ball.player.id === player.id
                })
                if (index >= 0) return
                const { ballColor, x, y } = player
                const ball = this.add.ball(x, y, 'ball', ballColor)
                ball.player = player
                this.physics.add.collider(layer, ball)


                this.balls.push(ball)
            }
        })
        this.moveKeys = this.input.keyboard?.addKeys('w,a,d')

        this.socketRef.current?.emit("finishLoad")
        console.log('this', this)
    }

    update(time: number, delta: number): void {
        if (this.frameNumberForUpdate < this.frameNumber - 10) {
            if (this.speed === 200) {
                this.speed = 800
                this.game.loop.stop()
                Phaser.Core.TimeStep.call(this.game.loop, this.game, {
                    target: 200,
                    forceSetTimeOut: true
                })
                this.game.loop.start(this.game.step.bind(this.game))
            }
            this.move(delta)
        } else if (this.frameNumberForUpdate < this.frameNumber - 6) {
            this.sendFrameOperation()
            if (this.speed === 800) {
                this.speed = 200
                this.game.loop.stop()
                Phaser.Core.TimeStep.call(this.game.loop, this.game, {
                    target: 50,
                    forceSetTimeOut: true
                })
                this.game.loop.start(this.game.step.bind(this.game))
            }

            this.move(delta)
        }
    }

    shutdown() {
        this.socketRef.current?.off('receiveOperation', this.receiveOperation)
        this.socketRef.current?.off('receiveFrameNumber', this.receiveFrameNumber)
        this.socketRef.current?.off('receiveBatchFrameOperation', this.receiveBatchFrameOperation)
    }

    receiveFrameNumber = (frameNumber: number) => {
        this.frameNumber = frameNumber
    }

    receiveOperation = (frameNumber: number, operations: Record<string, any>) => {
        this.frameNumberOperationMap.set(frameNumber, operations)
    }

    receiveBatchFrameOperation = ({ frameNumberForUpdate, frameOperations }: any) => {
        frameOperations.forEach((frameOpration: any, index: number) => {
            if (frameOpration) {
                this.frameNumberOperationMap.set(frameNumberForUpdate + index, frameOpration)
            }
        })
    }

    sendFrameOperation() {
        const { w, a, d } = this.moveKeys
        this.socketRef.current?.emit('sendOperation', { frameNumber: this.frameNumber, w: w.isDown ? 'down' : 'up', a: a.isDown ? 'down' : 'up', d: d.isDown ? 'down' : 'up' })
    }

    move(delta: number) {
        console.log(delta)
        const operations = this.frameNumberOperationMap.get(this.frameNumberForUpdate)
        if (operations == null) {
            if (this.lock === 'open') {
                this.socketRef.current?.emit('requestBatchFrameOperation', this.frameNumberForUpdate)
                this.lock = 'close'
                setTimeout(() => {
                    this.lock = 'open'
                }, 100)
            }
            return
        }
        Object.keys(operations).forEach(key => {
            const { w, a, d } = operations[key]
            const ball = this.balls?.find(ball => {
                return ball.player.id === key
            })
            // console.log(this.frameNumberForUpdate,w,a,d)
            if (w === 'down') {
                ball?.body.setVelocityY(-this.speed)
            } else if (ball?.body.blocked.down === false) {
                ball?.body.setVelocityY(this.speed / 2)
            }

            if (a === 'down') {
                ball?.body.setVelocityX(-this.speed)
            } else {
                if (d === 'down') {
                    ball?.body.setVelocityX(this.speed)
                } else {
                    ball?.body.setVelocityX(0)
                }
            }
        })
        this.frameNumberOperationMap.delete(this.frameNumberForUpdate)
        this.frameNumberForUpdate++
    }

}